#ifndef OPERATION_BOX_H_JM_20120627
#define OPERATION_BOX_H_JM_20120627

// ## C++
#include <sstream>
// ##
#include "../BufferedOutputBox.h"
#include "../BoxBaseTypes.h"

namespace jm4bbx {

	/**
	 * Sablona pre rodicovsku triedu pre aritmeticke operacie nad dvomi vektormi
	 * hodnot. Vygenerovana trieda obsahuje veskeru funkcionalitu a odvodene 
	 * triedy musia len definovat premennu BOX_ID, ktora identifikuje 
	 * box v modele vypoctu a doplnit konstruktor, ktory reflektuje vsetky 
	 * vyzadovane parametre vytvarania.
	 * 
	 * Za registraciu boxu do factory pre runtime je zodpovedna odvodena trieda.
	 * 
	 * @tparam OperationTrait  Zasada OperationTrait urcuje specificke 
	 * vlastnosti operacie. Zasadou operacie je trieda splnujuca kontrakt, ktory 
	 * vyzaduje definiciu typu politiky datoveho typu operacie a definiciu 
	 * vykonania operacie. Poziadavky demonstruje nasledujuci priklad:
	 * @code
	 *   // Zasada  pre scitanie celych cisel.
	 *  struct IntAddTraitExample {
	 *  
	 * 	  // Typ dat vstupujucich do boxu.
	 * 	  typedef bobox::int_policy operation_policy;
	 * 	
	 * 	   // Vykonanie operacie.
	 * 	   int performeOperation(int x, int y) const { 
	 * 	     return x + y; 
	 * 	   }
	 *  };
	 * @endcode
	 * @tparam RealBoxType Model realne pouzivaneho boxu - tj. odvodeneho boxu. 
	 **/
	template<class OperationTrait>
	class OperationBox : 
		public BufferedOneOutputBox<typename OperationTrait::value_type>,
		public OperationTrait {

	public:
		static const groupType INPUT_GROUP_1;
		static const groupType INPUT_GROUP_2;
		static const groupType OUTPUT_GROUP;	
		static const columnType INPUT_COLUMN_1;
		static const columnType INPUT_COLUMN_2;
		static const columnType OUTPUT_COLUMN;
		static const inType IN_ARC_1;
		static const inType IN_ARC_2;
		static const outType OUT_ARC;

		/**
		 * Model konecneho boxu pouzivany v modele vypoctu pre tvorbu boxov.
		 **/
		//typedef  RealBoxModel BoxModel;

		/**
		 * Konstruktor pouzivany z factory na boxy pre runtime.
		 ***/
		OperationBox(const BoxParamPack& pack) 
			: BufferedOneOutputBox(pack, OUTPUT_GROUP, OUTPUT_COLUMN, OUT_ARC) 
		{ }

		virtual void initImpl() {

			add_group(INPUT_GROUP_1, INPUT_COLUMN_1, 
				get_input_descriptors(IN_ARC_1));

			add_group(INPUT_GROUP_2, INPUT_COLUMN_2, 
				get_input_descriptors(IN_ARC_2));


			add_group(OUTPUT_GROUP, OUTPUT_COLUMN, 
				get_output_descriptors(OUT_ARC));
	
			initBufferedOutputs();
			//	allocateResultChunk();
			wait4Receive();
		}
		

		virtual void syncMachEtwas() {

			if (!inputGroup_) {
				inputGroup_ = getGroupPtr(INPUT_GROUP_1);
			}
			//if (ig.isPoisoned()/*poisoned_*/) {
			//	send_poisoned(OUT_ARC);
			//	return;
			//}

			int rpos = inputGroup_->getReadPosition();

			for (; inputGroup_->getReadPosition() < inputGroup_->getReadEnd() 
				&& !isOutputFull(); inputGroup_->moveReadPosition()) {
				
					//get_data<operation_policy>(OUTPUT_COLUMN)[ig.getReadPosition()] = 
					
					rpos = inputGroup_->getReadPosition();
					
					//std::stringstream stream;
					//stream << 
					//	performeOperation(
					//		get_data<operation_policy>(INPUT_COLUMN_1)[rpos],
				 //   		get_data<operation_policy>(INPUT_COLUMN_2)[rpos]);

					//addOutputData(stream.str());

					addOutputData(performeOperation(
						get_data<operation_policy>(INPUT_COLUMN_1)[rpos],
						get_data<operation_policy>(INPUT_COLUMN_2)[rpos]));

			}

			sendOutputData();

			//Group og = getGroup(OUTPUT_GROUP);
			//og.relocate(rpos, rend);
			//send(OUT_ARC, OUTPUT_GROUP);

			//rpos = ig.getReadPosition();
			//const int rend = ig.getReadEnd();


			if (!(inputGroup_->getReadPosition() < inputGroup_->getReadEnd())
				&& isOutputEmpty()) {
					sendSignalsOut();
					wait4Receive();
					inputGroup_.reset();
			}

		//	wait4Receive();

		}
		
	private:
		GroupPtr inputGroup_; 
		//const int RESULT_CHUNK_SIZE;
		//
		//void allocateResultChunk() {
		//	allocate(OUTPUT_GROUP, RESULT_CHUNK_SIZE);
		//	getGroup(OUTPUT_GROUP).resize(RESULT_CHUNK_SIZE);
		//}

		//void wait4Receive() {
		//	wait4SignalsIn();
		//	receive(IN_ARC, INPUT_GROUP);
		//}

		void wait4Receive() {
			receive(IN_ARC_1, INPUT_GROUP_1);
			receive(IN_ARC_2, INPUT_GROUP_2);
		}
	};

	template<class T>
	const  groupType OperationBox<T>::INPUT_GROUP_1 = groupType(0);
	template<class T>
	const  groupType OperationBox<T>::INPUT_GROUP_2 = groupType(1);
	template<class T>
	const groupType OperationBox<T>::OUTPUT_GROUP = groupType(2);
	template<class T>
	const  columnType OperationBox<T>::INPUT_COLUMN_1 = columnType(0);
	template<class T>
	const  columnType OperationBox<T>::INPUT_COLUMN_2 = columnType(1);
	template<class T>
	const columnType OperationBox<T>::OUTPUT_COLUMN = columnType(2);
	template<class T>
	const inType OperationBox<T>::IN_ARC_1 = inType(0);
	template<class T>
	const inType OperationBox<T>::IN_ARC_2 = inType(1);
	template<class T>
	const outType OperationBox<T>::OUT_ARC = outType(0);	

} // namespace jm4bbx



#endif
